---
title: Data model
summary: Wire/contract model and external persistence model for PdfAPI.
status: draft
owner: Not determined from source
last_reviewed: 2026-09-17
library: PdfAPI
reference_type: embedded
---

# Data model

There is **no repository-local persistence model**. The data model spans two boundaries: an in-repository wire/contract model, and an external, unvendored persistence model.

## Connections

The database connection used by the persistence model below is composed at run time; it is **not** a static connection string. See [Configuration: Runtime-resolved database connections](configuration.md#runtime-resolved-database-connections) for the full resolution call, environment key, and schema argument.

## Wire and contract model

Defined in `PdfAPI.Core`, in this repository. Shared by the API and the `PdfAPI.Client` package.

| Type | Kind | Purpose |
| --- | --- | --- |
| `PdfRequest` | Base request | Common request fields, including the `TrackingEnabled` getter forced on by `SendHeatEmailOnFailure` ([BR-PDFAPI-010](business-rules.md#br-pdfapi-010-tracking-forced-on-with-heat-email)) |
| `ConvertRequest` | Request (extends `PdfRequest`) | Convert operation payload; `SkipPdf` defaults to `true` |
| `ExtractPagesRequest` | Request (extends `PdfRequest`) | Extract operation payload |
| `MergeRequest` | Request (extends `PdfRequest`) | Merge operation payload |
| `DocumentRange` | Composition type | Pairs a `Document` with one or more `PageRange` values |
| `PageRange` | Composition type | Page range with clamping/reversal semantics ([BR-PDFAPI-018](business-rules.md#br-pdfapi-018-page-range-clamping-and-reversal)) |
| `Document` | Composition type | Document bytes plus metadata (e.g. file name) |
| `PdfOptions` | Options type | Merge/convert options, including `IsPdfa` and output format |
| `PdfResult` | Result type | `Status`, `Message`, `Document`, `TrackingId` |
| `PdfTrackingInfo` | Result type | `Status`, `FileId`, `FilePath`, `TrackingId` |
| `PdfTrackingMetadata` | Metadata type | Caller-supplied correlation metadata for tracking |
| `PdfFormat` | Enum | `Pdf`, `PdfA` |
| `PdfStatus` | Enum | Maps onto persisted status codes — see [Entity mappings](#entity-mappings) below |
| `PdfProcessorType` | Enum | Identifies which processor (ActivePDF/Syncfusion) produced a result |
| `SourceConversionType` | Enum | Identifies the source format class of a conversion |

Full member detail is in the [Endpoints](endpoints.md) request/response contract tables per action.

## Persistence model

Defined in `SAIF.Data.Model.PDF`, an external package not present in this repository.

**Table and column names must not be guessed.** No `.Column` mapping override, no `Table` name override, and no `.hbm.xml` mapping file exists in this repository — the `ClassMap<T>` mappings ship inside the external `SAIF.Data.Model.PDF` package. Physical schema detail (table names, column names, key generation, and nullability) is **Unknown\*\*** from this source and must be resolved from that package, not inferred from entity names.

| Entity | Table | Database | Identity | Associations | Mapping source |
| --- | --- | --- | --- | --- | --- |
| `PDFTracking` | Unknown** | Oracle (schema `IMG`**) | Unknown** (C# property `Id`, `long`, database-generated) | `StatusCode` → `PDFStatusCode`; `TrackingCode` → `PDFTrackingCode`; `ApplicationError` → `PDFApplicationErrorCode` (all many-to-one) | External — `SAIF.Data.Model.PDF` |
| `PDFStatusCode` | Unknown** | Oracle (schema `IMG`**) | Unknown** | Referenced by `PDFTracking.StatusCode` | External — `SAIF.Data.Model.PDF` |
| `PDFTrackingCode` | Unknown** | Oracle (schema `IMG`**) | Unknown** | Referenced by `PDFTracking.TrackingCode` | External — `SAIF.Data.Model.PDF` |
| `PDFApplicationErrorCode` | Unknown** | Oracle (schema `IMG`**) | Unknown** | Referenced by `PDFTracking.ApplicationError` | External — `SAIF.Data.Model.PDF` |

No `DELETE` is performed against any entity anywhere in this project (verified absence). Tracking rows are insert-and-update only; there is no purge/retention path in this codebase.

### PDFTracking properties observed in use

The following properties are the only inferable "column" evidence available for this entity.

| Property | C# type | Written / read | Value semantics observed |
| --- | --- | --- | --- |
| `Id` | `long` | Read (identity, query predicate) | Database-generated tracking id returned as `PdfResult.TrackingId` |
| `FileId` | `string` | Written on insert; read predicate | `Guid.NewGuid().ToString()` or caller-supplied `PdfTrackingMetadata.FileId` |
| `FilePrefix` | `string` | Written on insert | Two characters: `A`(convert)/`M`(merge) + `I`(imaged)/`E`(not imaged) |
| `StatusCode` | `PDFStatusCode` (FK) | Written on insert and update | Lookup for `SCHED`/`PROC`/`SUCC`/`ERROR`/`CANCE`/`ABORT` |
| `ApplicationError` | `PDFApplicationErrorCode` (FK) | Written on insert; read | Lookup by application code (`"AUTO"` for the convert/merge helpers) |
| `TrackingCode` | `PDFTrackingCode` (FK) | Written on insert; read | `CONV` or `MERGE` |
| `CreateDate` | `DateTime` | Written on insert | Client clock (`DateTime.Now`), not a server-side timestamp |
| `UpdateDate` | `DateTime` | Written on insert only — never refreshed on update | `DateTime.Now` |
| `CreateUser` | `string` | Written on insert | Hard-coded `"SYSTEM"` |
| `UpdateUser` | `string` | Written on insert only | Hard-coded `"SYSTEM"` |
| `SourceSystemId` | `string` | Written on insert | Caller-supplied correlation id |
| `OriginalFileExtension` | `string` | Written on insert | Source document extension |
| `IsPDFA` | `bool` | Written on insert | `outputFormat == PdfFormat.PdfA` |
| `ProcessStartDate` | `DateTime` | Written on insert only when `statusCode == "PROC"` | `DateTime.Now` |
| `ProcessEndDate` | `DateTime` | Written on update | `DateTime.Now` |
| `OutputFilePath` | `string` | Written on update (conditional); read on download | Composed path under `{pdfapi.rootdirectory}\{import\|merge}\out\<filename>` |
| `Message` | `string` | Written on update (conditional) | Failure/detail text, truncated to 300 characters client-side (inferred evidence of a `VARCHAR2(300)`-scale column, not a verified definition)* |

### Lookup entity properties observed in use

| Entity | Property | Usage |
| --- | --- | --- |
| `PDFStatusCode` | `Code` | Query predicate `Code.ToUpper() == statusCode` |
| `PDFTrackingCode` | `Code` | Query predicate `Code.ToUpper() == "CONV"/"MERGE"` |
| `PDFApplicationErrorCode` | `Code` | Query predicate `Code.ToLower() == applicationCode.ToLower()` |
| `PDFApplicationErrorCode` | `HeatEmailSubject` | Read for the incident email subject ([BR-PDFAPI-015](business-rules.md#br-pdfapi-015-incident-email-on-failure)) |

## Entity mappings

No `ClassMap<T>` mappings exist in this repository. The four entities above are declared and mapped entirely inside the external `SAIF.Data.Model.PDF` package. This repository has no `.hbm.xml` files, no `Table` name overrides, and no `.Column` mapping overrides. Any documentation of physical table or column names must be resolved from that package, not from this analysis.

## Stored procedures

No stored procedures or PL/SQL packages are referenced by this service; all database access is through NHibernate mappings and queries.

## ORM operations (traced to source; no SQL text exists in this repository)

| ID | CRUD | Operation | Entity | Enclosing method |
| --- | --- | --- | --- | --- |
| SQL-001 | Create | `pdfTrackingRepo.Add(trackingRecord)` inside `Begin()`/`Commit()` | `PDFTracking` | `CreateTrackingRecord` |
| SQL-002 | Read | `appErrorRepo.Query().SingleOrDefault(x => x.Code.ToLower() == applicationCode.ToLower())` | `PDFApplicationErrorCode` | `CreateTrackingRecord` |
| SQL-003 | Read | `pdfCacheStatusCodeRepo.Query().SingleOrDefault(x => x.Code.ToUpper() == statusCode)` | `PDFStatusCode` | `CreateTrackingRecord` and `UpdateTrackingRecord` |
| SQL-004 | Read | `pdfTrackingCodeRepo.Query().SingleOrDefault(x => x.Code.ToUpper() == ("CONV"\|"MERGE"))` | `PDFTrackingCode` | `CreateTrackingRecord` |
| SQL-005 | Read | `pdfTrackingRepo.Query().SingleOrDefault(x => x.Id == trackingId)` inside `Begin()`/`Commit()` | `PDFTracking` | `CheckPdfTrackingStatusById(long)` |
| SQL-006 | Read | `repo.Query().FirstOrDefault(x => x.FileId.ToLower() == fileId.ToLower())` | `PDFTracking` | `CreateTrackingRecord(ConvertRequest, string)` (idempotency probe) |
| SQL-007 | Read | `pdfTrackingRepo.Query().FirstOrDefault(x => x.Id == trackingId)` inside `Begin()`/`Commit()` | `PDFTracking` | `UpdateTrackingRecord` |
| SQL-008 | Update | Dirty-tracking flush at `Commit()` — no explicit `Update()` call; mutates `OutputFilePath`, `StatusCode`, `ProcessEndDate`, `Message` | `PDFTracking` | `UpdateTrackingRecord` |

Additional facts:
- `SingleOrDefault` (SQL-002/003/004/005) throws if the underlying lookup or tracking query returns more than one row.
- SQL-006 uses `FirstOrDefault`, tolerating duplicate `FileId` rows — no uniqueness is assumed on the file id.
- SQL-007/008 form a read-modify-write pair: the entity loaded by SQL-007 is mutated in place, and the `UPDATE` is emitted only by NHibernate's dirty-check flush at `Commit()` — correct only under a stateful session strategy.
- `record.StatusCode.Code` (SQL-005 caller) is dereferenced *after* `_uow.Commit()`; this is only safe if `PDFTracking.StatusCode` is mapped eager in the external package — inferred from runtime viability, not verified*.

## Related documentation
- [API index](index.md)
- [Overview](overview.md)
- [Architecture](architecture.md)
- [Endpoints](endpoints.md)
- [Authentication and authorization](authentication-authorization.md)
- [Dependencies](dependencies.md)
- [Configuration](configuration.md)
- [Operations and processes](operations-processes.md)
- [Business rules](business-rules.md)
- [Operational runbook](operational-runbook.md)
- [Tech debt](tech-debt.md)

> **Note:** Statements marked with `*` were inferred from code structure, naming conventions, or
> contextual evidence where direct confirmation was not available in source artifacts. Statements
> marked with `**` could not be confirmed; treat as unknown until verified.

## Sources
- PdfAPI\Services\PdfTrackingService.cs
- PdfAPI\App_Start\IocConfig.cs
- PdfAPI.Core\Request\PdfRequest.cs
- PdfAPI.Core\Request\ConvertRequest.cs
- PdfAPI.Core\Extract\PageRange.cs
- PdfAPI.Core\PdfResult.cs
